/* Juan Carlos Leon 
 * New York, New York 
 * Tuesday, April 21, 2009 
 * Prof. Roberto Jimeno. 
 
Game of Life Rules:

The universe of the Game of Life is an infinite two-dimensional grid of cells, 
each of which is either alive or dead. Cells interact with their eight neighbours, 
which are the cells that are directly horizontally, vertically, or diagonally adjacent. 
At each step in time, the following effects occur:

   1. Any live cell with fewer than two neighbours dies of loneliness.
   2. Any live cell with more than three neighbours dies of crowding.
   3. Any dead cell with exactly three neighbours comes to life.
   4. Any live cell with two or three neighbours lives, unchanged, to the next generation. 

0 1 1 
0 1 0 
0 0 0 

sum of all neighbors = 0 dies 
sum of all neighbors = 1 dies 
sum of all neighbors = 2 lives
sum of all neighbors = 3 lives
sum of all neighbors = 4 dies  

The initial pattern constitutes the first generation of the system. 
The second generation is created by applying the above rules simultaneously 
to every cell in the first generation -- births and deaths happen simultaneously, 
and the discrete moment at which this happens is called a tick. 
The rules continue to be applied repeatedly to create further generations.

*/
 
#include <cstdlib> 
#include <iostream>
#include <ctime>
#include <vector>
using namespace std;

int neighbors( int row, int column, vector< vector<int> >& matrix ) { 
 
    /* Check for out of boundaries in the matrix +1 J*/ 
    
 
    if ( row < 0 ) { 
        // set row to matrix.size()-1 which is the other side in the matrix.
        // if out of boundary. 
        row = matrix.size()-1; 
    } else if ( row>= matrix.size() ) { 
        // ohterwise is the first elment
        row = 0; 
    }
    

    if ( column < 0 ) { 
        // set column to matrix.size() which is the other side in the matrix.
        // if out of boundary.
        column = matrix.size()-1;   
    } else if (column >= matrix.size() ) {
        // ohterwise is the first elment
        column = 0; 
    }  
    
    
    return ( matrix[row][column] );     
} 

vector< vector<int> > populateGrid( vector< vector<int> > matrix, int grid, int generation ) { 
    for( int xy=1; xy < generation; xy++) { 
        matrix[rand() % grid][rand() % grid] = 1; 
    } 
    return matrix;  
} 

vector< vector<int> > printGrid( vector< vector<int> > matrix, int grid, int generation ) { 

    vector< vector<int> > BufferGrid( grid, vector<int>( grid, 0) );    

    int south, north, east, west; 
    int north_east, south_east; 
    int north_west, south_west;  
    int row; 
    int column = 0;  
    int counter = 0; 
    int vecinos = 0; 

    cout << endl;

    for(row=0; row < matrix.size()-1; row++) {
       
        for(column=0; column < matrix.size()-1; column++) {
            // counting the nodes in a row 
            counter++;  

            //cout << matrix[row][column];  
            if (matrix[row][column]==0) {
                cout << " ";
            } else {
                cout << "*";
            }
                 north = neighbors(row-1, column, matrix); 
                 south = neighbors(row + 1, column, matrix);    
                 east  = neighbors(row, column+1, matrix);  
                  west = neighbors(row, column-1, matrix);  
            north_east = neighbors(row-1, column+1, matrix);    
            north_west = neighbors(row-1, column-1, matrix);    
            south_east = neighbors(row+1, column+1, matrix);   
            south_west = neighbors(row+1, column-1, matrix);   

            vecinos = north+south+east+west+north_east+north_west+south_east+south_west; 

            // Apply rules 
            if (matrix[row][column] == 1 && vecinos < 2) { 
                BufferGrid[row][column] = 0; 
            } else if (matrix[row][column] == 1 && vecinos > 3) { 
                BufferGrid[row][column] = 0; 
            } else if (matrix[row][column] == 1 && (vecinos == 2 || vecinos == 3)) { 
                BufferGrid[row][column] = 1; 
            } else if (matrix[row][column] == 0 && vecinos == 3) { 
                BufferGrid[row][column] = 1; 
            }  

            if ( counter < grid ) { 
                cout << " ";
            }    
              
        }

        // reset the counter to avoid printing "-" at the 
        // end of the row. 
        counter = 0; 

        // let's go to the next row.           
        cout << endl;  
    }

    return BufferGrid; 
}  


void Grid( int gridSize, int generation ) {

    vector< vector<int> > tmp_matrix; // empty; not initialize vector; holds next generation.  
    // Initialize matrix; set values to zero;     
    vector< vector<int> > LifeGrid(gridSize, vector<int>(gridSize,0)); 

    LifeGrid = populateGrid( LifeGrid, gridSize, generation ); 

    while (true) {
        tmp_matrix = printGrid( LifeGrid, gridSize, generation );  
        LifeGrid = printGrid( tmp_matrix, gridSize, generation );
        sleep(1); 
    }  
} 



main() {
   int grid_size = 50;   

   // Good number to start with
   int generationOne = 1250;
   //The static_cast is to keep some compilers quite, 
   //as they may issue warnings about loss of precision
   //seed the pseudo random number generator with some value.
   srand( static_cast<unsigned int>( time(0) ));
   Grid( grid_size, generationOne );  

}





